﻿/*
	Copyright 2010 Intuitive Solutions

	Licensed under the Apache License, Version 2.0 (the "License");
	you may not use this file except in compliance with the License.
	You may obtain a copy of the License at

	   http://www.apache.org/licenses/LICENSE-2.0

	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
*/

using System;
using System.IO;
using System.Xml;
using Metamorph.Core.Bpm;
using Metamorph.Configuration;
using Metamorph.Core.Bpm.Events;
using System.Collections.Generic;
using Metamorph.Core.Bpm.Gateways;
using Metamorph.Core.Bpm.Activities;
using Metamorph.Core.Bpm.Connectors;

namespace Metamorph.Core
{
	public class BusinessProcessProvider
	{
		private Dictionary<string, IShape>				dShapes;
		private Dictionary<string, List<Transition>>	dTargetShapes;
		private Subprocess								pBusinessProcess;
		private XmlTextReader							xtrXmlTextReader;
		private string									sBusinessProcessName;

		public BusinessProcessProvider()
		{
		}

		public Subprocess BusinessProcess
		{
			get
			{
				return pBusinessProcess;
			}
		}

		public void Load(string sBusinessProcessName)
		{
			FileStream srFile;
            string sFilePath = MetamorphConfiguration.Instance.StateMachineDefinition;

			this.sBusinessProcessName = sBusinessProcessName;

			using (srFile = new FileStream(sFilePath, System.IO.FileMode.Open, FileAccess.Read))
			{
				xtrXmlTextReader = new XmlTextReader(srFile);

				xtrXmlTextReader.XmlResolver = null;

				xtrXmlTextReader.Read();
				xtrXmlTextReader.MoveToContent();

				TraverseXml();

				xtrXmlTextReader.Read();
				xtrXmlTextReader.MoveToContent();
			}
		}

		public void TraverseXml()
		{
			if ("metamorph".Equals(xtrXmlTextReader.Name.ToLower()))
			{
				IgnoreBusinessProcessDefinition();

				LoadBusinessProcess();
			}
		}

		public void LoadBusinessProcess()
		{
			bool bIgnoreBusinessProcess = false;

			if ("process".Equals(xtrXmlTextReader.Name.ToLower()))
			{
				while ("process".Equals(xtrXmlTextReader.Name.ToLower()) &&
						pBusinessProcess == null)
				{
					for (int i = 0; i < xtrXmlTextReader.AttributeCount; i++)
					{
						xtrXmlTextReader.MoveToNextAttribute();

						if ("name".Equals(xtrXmlTextReader.Name))
						{
							if (!xtrXmlTextReader.Value.Equals(sBusinessProcessName))
							{
								bIgnoreBusinessProcess = true;

								break;
							}
							else
							{
								dShapes				= new Dictionary<string, IShape>();
								pBusinessProcess	= new Subprocess(sBusinessProcessName);
								dTargetShapes		= new Dictionary<string,List<Transition>>();

								pBusinessProcess.Transition			= new Transition();
								pBusinessProcess.Transition.Source	= pBusinessProcess;
							}
						}

						xtrXmlTextReader.ReadAttributeValue();
					}

					xtrXmlTextReader.Read();
					xtrXmlTextReader.MoveToContent();

					if (bIgnoreBusinessProcess)
					{
						IgnoreBusinessProcessDefinition();

						bIgnoreBusinessProcess = false;
					}
					else
					{
						LoadProcess(pBusinessProcess);
					}
				}
			}
		}

		private void IgnoreBusinessProcessDefinition()
		{
			while (!("process".Equals(xtrXmlTextReader.Name.ToLower()) &&
					xtrXmlTextReader.IsStartElement()) &&
					!xtrXmlTextReader.EOF)
			{
				xtrXmlTextReader.Read();
				xtrXmlTextReader.MoveToElement();
			}
		}

		private void LoadProcess(ISubprocess pBusinessProcess)
		{
			while ("event".Equals(xtrXmlTextReader.Name.ToLower()) ||
					"task".Equals(xtrXmlTextReader.Name.ToLower()) ||
					"gateway".Equals(xtrXmlTextReader.Name.ToLower()) ||
					"subprocess".Equals(xtrXmlTextReader.Name.ToLower()))
			{
				if ("event".Equals(xtrXmlTextReader.Name.ToLower()))
				{
					IEvent eEvent = LoadEvent();

					pBusinessProcess.Events.Add(eEvent);

					if (EventType.Start.Equals(eEvent.EventType))
					{
						pBusinessProcess.InitialShape = eEvent;
					}
				}
				else if ("task".Equals(xtrXmlTextReader.Name.ToLower()))
				{
					LoadTasks();
				}
				else if ("gateway".Equals(xtrXmlTextReader.Name.ToLower()))
				{
					LoadGateway();
				}
				else
				{
					LoadSubprocess();
				}

				if (!xtrXmlTextReader.IsStartElement())
				{
					xtrXmlTextReader.Read();
					xtrXmlTextReader.MoveToContent();
				}
			}
		}

		private void LoadGateway()
		{
			string		sName		= null;
			IGateway	gGateway	= null;

			for (int i = 0; i < xtrXmlTextReader.AttributeCount; i++)
			{
				xtrXmlTextReader.MoveToNextAttribute();

				if ("name".Equals(xtrXmlTextReader.Name))
				{
					sName = xtrXmlTextReader.Value;
				}

				xtrXmlTextReader.ReadAttributeValue();
			}

			xtrXmlTextReader.Read();
			xtrXmlTextReader.MoveToContent();

			gGateway = new ExclusiveGateway(sName);

			AddShapes(gGateway);

			while ("connector".Equals(xtrXmlTextReader.Name.ToLower()))
			{
				Transition tConnector = LoadConnector();

				gGateway.Transitions.Add(tConnector);

				if (string.IsNullOrEmpty(tConnector.Condition))
				{
					string sConnectorName = !string.IsNullOrEmpty(tConnector.Name) ? tConnector.Name : tConnector.Target.Name;

					tConnector.Condition = sConnectorName.Replace(" ", "") + "Condition";
				}
			}
		}

		private IActivity LoadTasks()
		{
			ITask						tTask		= null;
			Dictionary<string, string>	dProperties	= null;

			string sName = null, sActionName = null, sActivityType = null;

			for (int i = 0; i < xtrXmlTextReader.AttributeCount; i++)
			{
				xtrXmlTextReader.MoveToNextAttribute();

				if ("name".Equals(xtrXmlTextReader.Name))
				{
					sName = xtrXmlTextReader.Value;
				}
				else if ("type".Equals(xtrXmlTextReader.Name))
				{
					sActivityType = xtrXmlTextReader.Value;
				}

				xtrXmlTextReader.ReadAttributeValue();
			}

			xtrXmlTextReader.Read();
			xtrXmlTextReader.MoveToContent();

			if ("property".Equals(xtrXmlTextReader.Name.ToLower()))
			{
				dProperties = LoadProperties();
			}

			tTask = ActivityFactory.Instance.CreateTask(sName, sActionName, sActivityType, dProperties);

			AddShapes(tTask);

			if ("event".Equals(xtrXmlTextReader.Name.ToLower()))
			{
				tTask.Events = new List<IEvent>();

				while ("event".Equals(xtrXmlTextReader.Name.ToLower()))
				{
					IEvent eEvent = LoadEvent();
					tTask.Events.Add(eEvent);

					if (EventType.Start.Equals(eEvent.EventType))
					{
						eEvent.Transition.Target = tTask;
					}

					xtrXmlTextReader.Read();
					xtrXmlTextReader.MoveToContent();
				}
			}

			tTask.Transition = LoadConnector();

			xtrXmlTextReader.Read();
			xtrXmlTextReader.MoveToContent();

			tTask.Transition.Source = tTask;

			return tTask;
		}

		private Dictionary<string, string> LoadProperties()
		{
			string						sPropertyName	= null, sPropertyValue = null;
			Dictionary<string, string>	dProperties		= new Dictionary<string, string>();

			while ("property".Equals(xtrXmlTextReader.Name.ToLower()))
			{
				for (int i = 0; i < xtrXmlTextReader.AttributeCount; i++)
				{
					xtrXmlTextReader.MoveToNextAttribute();

					if ("name".Equals(xtrXmlTextReader.Name))
					{
						sPropertyName = xtrXmlTextReader.Value;
					}
					else if ("value".Equals(xtrXmlTextReader.Name))
					{
						sPropertyValue = xtrXmlTextReader.Value;
					}

					xtrXmlTextReader.ReadAttributeValue();
				}

				dProperties.Add(sPropertyName, sPropertyValue);

				xtrXmlTextReader.Read();
				xtrXmlTextReader.MoveToContent();
			}

			return dProperties;
		}

		private void LoadSubprocess()
		{
			ISubprocess sSubprocess		= null;
			bool		bIsCollapsed	= false;
			string		sName			= null, sActivityType = null;

			Dictionary<string, string> dProperties = null;

			for (int i = 0; i < xtrXmlTextReader.AttributeCount; i++)
			{
				xtrXmlTextReader.MoveToNextAttribute();

				if ("name".Equals(xtrXmlTextReader.Name))
				{
					sName = xtrXmlTextReader.Value;
				}
				else if ("type".Equals(xtrXmlTextReader.Name))
				{
					sActivityType = xtrXmlTextReader.Value;
				}
				else if ("isCollapsed".Equals(xtrXmlTextReader.Name))
				{
					if ("true".Equals(xtrXmlTextReader.Value.ToLower()))
					{
						bIsCollapsed = true;
					}
				}

				xtrXmlTextReader.ReadAttributeValue();
			}

			xtrXmlTextReader.Read();
			xtrXmlTextReader.MoveToContent();

			if ("property".Equals(xtrXmlTextReader.Name.ToLower()))
			{
				dProperties = LoadProperties();
			}

			if (bIsCollapsed)
			{
				sSubprocess = ActivityFactory.Instance.CreateSubprocessReference(MetamorphManager.Instance.GetBusinessProcessDefinition(sName), sActivityType, dProperties);
			}
			else
			{
				sSubprocess = ActivityFactory.Instance.CreateSubprocess(sName, sActivityType, dProperties);
			}

			AddShapes(sSubprocess);

			if (bIsCollapsed)
			{
				SubprocessReference srSubprocessReference = (SubprocessReference)sSubprocess;

				if ("event".Equals(xtrXmlTextReader.Name.ToLower()))
				{
					srSubprocessReference.Events = new List<IEvent>();

					while ("event".Equals(xtrXmlTextReader.Name.ToLower()))
					{
						srSubprocessReference.Events.Add(LoadEvent());

						xtrXmlTextReader.Read();
						xtrXmlTextReader.MoveToContent();
					}
				}

				sSubprocess.Transition = LoadConnector();

				xtrXmlTextReader.Read();
				xtrXmlTextReader.MoveToContent();
			}
			else
			{
				LoadProcess(sSubprocess);

				sSubprocess.Transition = LoadConnector();
			}

			sSubprocess.Transition.Source = sSubprocess;
		}

		private IEvent LoadEvent()
		{
			IEvent						eEvent		= null;
			Dictionary<string, string>	dProperties	= null;
			EventType					etEventType	= EventType.Start;
			string						sEventName	= null, sTrigger = null;

			for (int i = 0; i < xtrXmlTextReader.AttributeCount; i++)
			{
				xtrXmlTextReader.MoveToNextAttribute();

				if ("name".Equals(xtrXmlTextReader.Name))
				{
					sEventName = xtrXmlTextReader.Value;
				}
				else if ("type".Equals(xtrXmlTextReader.Name))
				{
					etEventType = (EventType)Enum.Parse(typeof(EventType), xtrXmlTextReader.Value);
				}
				else if ("trigger".Equals(xtrXmlTextReader.Name))
				{
					sTrigger = xtrXmlTextReader.Value;
				}

				xtrXmlTextReader.ReadAttributeValue();
			}

			xtrXmlTextReader.Read();
			xtrXmlTextReader.MoveToContent();

			if ("property".Equals(xtrXmlTextReader.Name.ToLower()))
			{
				dProperties = LoadProperties();
			}

			eEvent = EventFactory.Instance.CreateEvent(sEventName, sTrigger, etEventType, dProperties);

			AddShapes(eEvent);

			eEvent.Transition = LoadConnector();

			eEvent.Transition.Source = eEvent;

			return eEvent;
		}

		private Transition LoadConnector()
		{
			Metamorph.Core.Bpm.Connectors.Transition	tConnector = null;
			Dictionary<string, string>					dProperties = null;

			if ("connector".Equals(xtrXmlTextReader.Name.ToLower()))
			{
				string sConnectorName = null, sTarget = null;

				for (int i = 0; i < xtrXmlTextReader.AttributeCount; i++)
				{
					xtrXmlTextReader.MoveToNextAttribute();

					if ("name".Equals(xtrXmlTextReader.Name))
					{
						sConnectorName = xtrXmlTextReader.Value;
					}
					else if ("target".Equals(xtrXmlTextReader.Name))
					{
						sTarget = xtrXmlTextReader.Value;
					}

					xtrXmlTextReader.ReadAttributeValue();
				}

				xtrXmlTextReader.Read();
				xtrXmlTextReader.MoveToContent();

				if ("property".Equals(xtrXmlTextReader.Name.ToLower()))
				{
					dProperties = LoadProperties();

					xtrXmlTextReader.Read();
					xtrXmlTextReader.MoveToContent();
				}

				tConnector = new Metamorph.Core.Bpm.Connectors.Transition(sConnectorName);

				if (dProperties != null)
				{
					if (dProperties.ContainsKey("Condition"))
					{
						tConnector.Condition = dProperties["Condition"];
					}
				}

				IShape sShape = GetShape(sTarget);

				if (sShape != null)
				{
					tConnector.Target = sShape;
				}
				else
				{
					if (!dTargetShapes.ContainsKey(sTarget))
					{
						dTargetShapes.Add(sTarget, new List<Metamorph.Core.Bpm.Connectors.Transition>());
					}

					dTargetShapes[sTarget].Add(tConnector);
				}
			}

			if (tConnector == null)
			{
				tConnector = new Transition();
			}

			return tConnector;
		}

		public IShape GetShape(string sStateName)
		{
			IShape sState = null;

			if (dShapes.ContainsKey(sStateName))
			{
				sState = dShapes[sStateName];
			}

			return sState;
		}

		private void AddShapes(IShape sShape)
		{
			dShapes.Add(sShape.Name, sShape);

			if (dTargetShapes.ContainsKey(sShape.Name))
			{
				foreach (Metamorph.Core.Bpm.Connectors.Transition tTransition in dTargetShapes[sShape.Name])
				{
					tTransition.Target = sShape;
				}

				dTargetShapes.Remove(sShape.Name);
			}
		}
	}
}
